#include <stdio.h>
#include <poll.h>
#include <errno.h>
#include "udk_os.h"
#include "commapi.h"
#include "commpro.h"
#include "cmds.h"
#include "minilzo.h"
//#define COMM_TCP
#include "strlist.h"
#ifndef OFFSETOF
#define OFFSETOF(type, field)    ((unsigned long) &(((type *) 0)->field))
#endif

typedef void (UDK_API* PCBUDKDownloadProcess)(int type, int length);



typedef struct _CmdHdr_{
	unsigned short Command, CheckSum, SessionID, ReplyID;
}TCmdHeader, *PCmdHeader;

static int LastError=0;
class CCommproThread;

 
typedef struct _HandleHeader{
	UDK_HANDLE cHandle;
	UDK_HMODULE cLibHandle; 
	ZKECOMM_INIT cInit;
	ZKECOMM_CLOSE cClose;
	ZKECOMM_SEND cSend;
	ZKECOMM_READ cRead;
	ZKECOMM_TERMINALREAD cTerminalRead;
	ZKECOMM_NAME	cName;
	ZKECOMM_FLUSH cFlush;

	//reader thread related 
	CCommproThread* ReadThread;
	Mutex* requestmutex;
    Condition* requestcond;
	int command;

	ReadCallBack ReadCallBackFunc; 
	unsigned short SessionID, ReplyID;
	int ReplyLen;
	int DataDelay;
	int BlockSize;

	//internal buffer
	char *RecvBuffer; //buffer to receive command response
	int RecvBufferLength;
	char *SendBuffer;
	int SendBufferLength;
	
	void *Param;
	//dialing number 
	char Telephone[20];//dsl
	int Dialup;	
	int ATCmdLength;
	int ATCMD;
	int atresponse;

	int PullMode;  //1(UDK_TRUE):PullMode,0(False):Push mode,PullMode for 485 and Ethernet,PushMode use for Ethernet only
	ZKECOMM_SENDATCMDTOMODEM cSendATCmdToModem;
	ZKECOMM_READATCMDFROMMODEM cReadATCmdFromModem;
	ST_COMM_PARAMS commparams;
	
	
	/*cache related*/
	PStrList slOptionCache;
	int SupportRRQExtend;
    int RRQProbed;
	char* sFwVersion;
	int aFreeSize[50];
	unsigned short lastCommand;
	PCBUDKDownloadProcess pCBDownloadProcess;
}THandleHeader, *PHandleHeader;

//dsl 2007.7.5
#define GPRS 5000
#define ATCMD_AT (GPRS + 0)      //AT����.����MODEM���Ƿ�׼����.
#define ATCMD_ATD (GPRS +1)      //��������.�磺ATD075538481234
#define ATCMD_CMDMODE (GPRS + 2) //MODEM��������ģʽ���������ǣ�+++
#define ATCMD_ATH (GPRS + 3)     //�Ҷ�MODEM.��:ATH
#define ATCMD_ERR -100


static unsigned short in_chksum(char *p, int len)
{
	unsigned long sum=0;
	
	while(len > 1) {
		sum += *((unsigned short*)p); p+=2;
		if(sum&0x80000000)
			sum = (sum & 0xFFFF) + (sum >> 16);
		len -= 2;
	}
	
	if(len)
		sum += (unsigned short) *(unsigned char*) p;
	
	while(sum >> 16)
		sum = (sum & 0xFFFF) + (sum >> 16);
	
	return ~sum;
}

static int MakeKey(int Key, int SessionID)
{
	int k,i;
	UDK_U8 B;
	UDK_U16 swp;
	k=0;
	for(i=0;i<32;i++)
		if(Key & (1<<i))
			k=(k<<1 | 1);
		else
			k=k<<1;
	k+=SessionID;

	((UDK_U8*)&k)[0]^='Z';
	((UDK_U8*)&k)[1]^='K';
	((UDK_U8*)&k)[2]^='S';
	((UDK_U8*)&k)[3]^='O';

	swp=((UDK_U16 *)&k)[0];
	((UDK_U16 *)&k)[0]=((UDK_U16 *)&k)[1];
	((UDK_U16 *)&k)[1]=swp;

	//_RPT3(_CRT_WARN, "Key: %d,%d->%d \n", Key, SessionID, k);
	
	B=(UDK_U8)(0xFF & udk_get_ticks());
	((UDK_U8*)&k)[0]^=B;
	((UDK_U8*)&k)[1]^=B;
	((UDK_U8*)&k)[2]=B;
	((UDK_U8*)&k)[3]^=B;

	return k;
}


const char * GetCmdName(unsigned short cmd)
{
	static char NameBuffer[20];
	switch(cmd)
	{
	case CMD_PREPARE_DATA: return "CMD_PREPARE_DATA";
	case CMD_DATA: return "CMD_DATA";
	case CMD_DB_RRQ: return "CMD_DB_RRQ";
	case CMD_USER_WRQ: return "CMD_USER_WRQ";
	case CMD_USERTEMP_RRQ: return "CMD_USERTEMP_RRQ";
	case CMD_USERTEMP_WRQ: return "CMD_USERTEMP_WRQ";
	case CMD_APPEND_USERTEMP: return "CMD_APPEND_USERTEMP";
	case CMD_ACK_OK: return "CMD_ACK_OK";
	case CMD_ACK_ERROR: return "CMD_ACK_ERROR";
	case CMD_ACK_DATA: return "CMD_ACK_DATA";
	case CMD_ACK_REPEAT: return "CMD_ACK_REPEAT";
	case CMD_ACK_UNKNOWN: return "CMD_ACK_UNKNOWN";
	case CMD_ACK_NO: return "CMD_ACK_NO";
	case CMD_ACK_ERROR_CMD: return "CMD_ACK_ERROR_CMD";
	case CMD_ACK_ERROR_INIT: return "CMD_ACK_ERROR_INIT";
	case CMD_ACK_ERROR_DATA: return "CMD_ACK_ERROR_DATA";
	case CMD_CONNECT: return "CMD_CONNECT";
	case CMD_EXIT: return "CMD_EXIT";
	case CMD_ENABLEDEVICE: return "CMD_ENABLEDEVICE";
	case CMD_DISABLEDEVICE: return "CMD_DISABLEDEVICE";
	case CMD_OPTIONS_RRQ: return "CMD_OPTIONS_RRQ";
	case CMD_GET_FREE_SIZES: return "CMD_GET_FREE_SIZES";
	case CMD_APPEND_USER: return "CMD_APPEND_USER";
	case CMD_ATTLOG_RRQ:  return "CMD_ATTLOG_RRQ";
	case CMD_DELETE_USER: return "CMD_DELETE_USER";
	case CMD_OPTIONS_WRQ: return "CMD_OPTIONS_WRQ";
	case CMD_FREE_DATA	 :return "CMD_FREE_DATA";
	case CMD_ACK_RETRY   :return "CMD_ACK_RETRY";
	case CMD_AUTH		 :return "CMD_AUTH";
	case CMD_ACK_UNAUTH	 :return "CMD_ACK_UNAUTH";
	case CMD_QUERY_DATA		 :return "CMD_QUERY_DATA";	
	case CMD_READ_DATA		 :return "CMD_READ_DATA";
	case CMD_GET_VERSION	 :return "CMD_GET_VERSION";
	case CMD_REG_EVENT		 :return "CMD_REG_EVENT";
	case CMD_MCU_COMMAND	 :return "CMD_MCU_COMMAND";
	case CMD_GET_PINWIDTH	 :return "CMD_GET_PINWIDTH";
	case CMD_UNLOCK			 :return "CMD_UNLOCK";
	default:
		sprintf(NameBuffer, "%d", cmd);
		return NameBuffer;
	}
}
#undef PRIME_TBLSIZ
#define PRIME_TBLSIZ 65536*255

static unsigned int hashpjw(const void *key, int keySize)
{
	unsigned char     *ptr=(unsigned char *)key;
	unsigned int       val=0;
	while (keySize--) {
	   int tmp;
	   val = (val << 4) + (*ptr);
	   if ((tmp = (val & 0xf0000000))!=0) {
		  val = val ^ (tmp >> 24);
		  val = val ^ tmp;
	   }
	   ptr++;
	}
	return val % (PRIME_TBLSIZ-1);
}

static int GetSeparatorIndex(char *Value)
{
	int result = 0;
	int i = 0;
	int j = 0;
	if (Value)
	{
		for(i; i < strlen(Value); i++)
		{
			if ((Value[i] == 13) || (Value[i] == 10))
			{
				j++;
				if (3 == j)
				{
					result = i + 1;
					break;
				}
			}
		}
	}
	return result;
}


static int comm_module_interface_init(PHandleHeader h,const char* commlib){
	int err=0;	
	PST_COMM_MODULE module=find_comm_module_by_library(commlib);
	//legacy comms.dll compatibilitly
	if(!module&&commlib&&!strcmp("comms.dll",commlib)){
		module = find_comm_module_by_library("udpcomm");
	}
	//firstly we try built-in comm interfaces
	if(module){
		h->cInit = module->comm_init;
		h->cClose=module->comm_close;
		h->cSend=module->comm_send;
		h->cRead=module->comm_read;
		h->cTerminalRead=module->comm_terminalread;
		h->cName=module->comm_name;
		h->cSendATCmdToModem=module->comm_send_at_to_modem;
		h->cReadATCmdFromModem=module->comm_read_at_from_modem;
		h->cLibHandle = NULL;
	}else if(NULL==(h->cLibHandle=udk_dlopen(commlib,0))){
		const char *pszErr = udk_dlerr();
		UDK_LOGW("UDK loading library[%s] error[%s]\n",commlib,pszErr?pszErr:"unknown");
		err = ZKEMSDK_ERR_GETPROC;
		return err;
	}else {	
		UDK_LOGD("loading %s okay\n",commlib?commlib:"null");
		h->cInit=(ZKECOMM_INIT)udk_dlsym(h->cLibHandle,"EMBUDP_INIT");
		h->cClose=(ZKECOMM_CLOSE)udk_dlsym(h->cLibHandle,"EMBUDP_CLOSE");
		h->cSend=(ZKECOMM_SEND)udk_dlsym(h->cLibHandle,"EMBUDP_SEND");
		h->cRead=(ZKECOMM_READ)udk_dlsym(h->cLibHandle,"EMBUDP_READ");
		h->cTerminalRead=(ZKECOMM_TERMINALREAD)udk_dlsym(h->cLibHandle,"EMBUDP_TERMINALREAD");
		h->cName=(ZKECOMM_NAME)udk_dlsym(h->cLibHandle,"EMBUDP_NAME");	
		h->cSendATCmdToModem=(ZKECOMM_SENDATCMDTOMODEM)udk_dlsym(h->cLibHandle,"EMBUDP_SENDTOMODEM");
		h->cReadATCmdFromModem=(ZKECOMM_READATCMDFROMMODEM)udk_dlsym(h->cLibHandle,"EMBUDP_READFROMMODEM");
	}
	if(!h->cInit || !h->cClose|| !h->cSend|| !h->cRead)
	{
		err = ZKEMSDK_ERR_GETPROC;
	}
	
	return err;
}


class CCommproThread:public Thread{
public:
	/*
	 * state transition
	 *
	 *                           ------------------------------------------
	 *                          \/										   |
	 * stopped----->started---->synccommand--->waitingresponse--->periodicevent
	 *    /|\     start     /\           		/\									|
	 *	-------------|			        |-----------------------------------
	*/
	enum{
		kStateStopped=0,//thread not runningg
		kStateStarting,//thread is starting
		kStateStarted,//thread is started
		kStateSyncCommand,//stop period monitor and switch to sync mode
		kStateSyncCommandServiced,//got response
		kStatePeriodicEvent,//only used in buffer mode
		kStateStopping,//thread is stopping
	};
	CCommproThread(PHandleHeader ph):
	Thread(false),
	m_ph(ph){
		m_bATmode = strlen(ph->Telephone)?true:false;
		m_bRegEvent = false;
		m_WorkerState = m_NextWorkerState =  kStateStopped;
		cmdtimeout = 1000;//ms
	}
	virtual ~CCommproThread(){
		requestExitAndWait();
		delete [] recvbuf4periodic;
	}
	
	void start(){
		try{
			recvbuflength = MAX_BUFFER_LEN_TCP;
			recvbuf4periodic = new char[MAX_BUFFER_LEN_TCP];
		}catch(std::bad_alloc){
			UDK_LOGE("out of memory?failed for periodic memory\n");
			exit(0);
		}
		int waittime=100;//ms
		//starting is intermediate state
		m_WorkerState = m_NextWorkerState = kStateStarting;
		run();
		while(m_WorkerState!=kStateStarted){
			udk_msleep(10);
			waittime--;
			if(waittime==0) break;
		}
		if(m_WorkerState!=kStateStarted){
			UDK_LOGE("\n\nFIXMEfailed to start commpro thread\n\n\n");
		}
			
	}
	//one-time initializations
	status_t    readyToRun()
	{
		if (pipe(readerCmdFds)) {
			   UDK_LOGE("Failed to create pipe: %s\n", 
					strerror(errno));
			   return ~NO_ERROR;
		}
		readerClosed = 0;
		//default is started 
		m_WorkerState = m_NextWorkerState = kStateStarted;
		return NO_ERROR;
	}
	int request_worker_state(int newWokerState)
	{
		m_NextWorkerState = newWokerState;		
		/* Kick readerloop. */
		int written = write(readerCmdFds[1], "x", 1);		
		if (written < 0){
		  UDK_LOGE("FAILED to kick readerloop!\n");
		}
		return written;
	}
	void lock_worker(){
		commandmutex.lock();
	}
	void unlock_worker(){
		commandmutex.unlock();
	}

	int get_worker_state()
	{
		return m_WorkerState;
	}
	status_t wait_worker_state(int newWokerState,int timeout_ms)
	{
		status_t err=NO_ERROR;
		cmdtimeout = timeout_ms;
		while (newWokerState!=m_WorkerState) {
			if (timeout_ms != 0) {
				err = commandcond.waitRelative(commandmutex,(long long)(((nsecs_t)timeout_ms)*1000000));
			} else
				err = commandcond.wait(commandmutex);
		}
		//reset worker state
		m_WorkerState = kStateStarted;
		//UDK_LOGD("reset state m_WorkerState=%d,waitstate=%d,nextwokerstate=%d\n",m_WorkerState,newWokerState,m_NextWorkerState);
		return err;
	}

	
	bool run_atmode_loop(PHandleHeader h)
	{
		bool bStopLoop=false;
		int ret;
		ret = wait_event(1000);
		if(ret>0){
			commandmutex.lock();
			//got event
			if(m_NextWorkerState!=m_WorkerState){
				if(kStateSyncCommand==m_NextWorkerState){
					int timeout=cmdtimeout;
					int size;			
					if(h->ATCMD == ATCMD_ATD) timeout = 30000;
					size = h->cReadATCmdFromModem(h->cHandle, h->RecvBuffer,h->ATCmdLength/* h->RecvBufferLength*/, timeout);
					if (size > 0){
						h->atresponse++;
						m_WorkerState = m_NextWorkerState = kStateSyncCommandServiced;
						commandcond.signal();
					}else{
						//timeout or invalid command without response,
						//anyway ,just serviced
						m_WorkerState = m_NextWorkerState = kStateSyncCommandServiced;
						commandcond.signal();
					}
				}else if(kStateStopping==m_NextWorkerState){					
					bStopLoop = true;					
					m_WorkerState = m_NextWorkerState = kStateStopped;					
					commandcond.signal();
				}
				
			}

			commandmutex.unlock();
		}else if(ret<0){
			UDK_LOGW("wait event error %d in at mode\n",ret);
			bStopLoop = true;
		}

		return bStopLoop==false;
	}

	bool run_buffer_mode(PHandleHeader h)
	{
		bool bStopLoop=false;
		int ret;
		ret = wait_event(200);
		if(ret>0){
			commandmutex.lock();
			do	//read correct data, throw away invalid data(pchd->ReplyID < h->ReplyID-1)
			{
				int size = 0;	
				int timeout=cmdtimeout;
				PCmdHeader pchd=(PCmdHeader)(h->RecvBuffer);
				if(m_NextWorkerState!=m_WorkerState){
					if(kStateSyncCommand==m_NextWorkerState){
						UDK_LOGI("start read timeout=%dms\n",timeout);
						size=h->cRead(h->cHandle, h->RecvBuffer, h->RecvBufferLength, timeout);
						UDK_LOGI("read finished\n");
						if (size > 0){						
							int cs = pchd->CheckSum;
							pchd->CheckSum = 0;//to avoid repeated crc?
							//check crc
							if(cs!=in_chksum(h->RecvBuffer, size)){
								UDK_LOGW("->Bad Checksum!!!\n");
							}else if(pchd->Command==CMD_REG_EVENT){
								service_callback(h,pchd,(char*)(pchd+1),size-sizeof(TCmdHeader));
								//turn regevent periodic access
								UDK_LOGW("enable regevent monitor\n");
								m_bRegEvent = true;
							}else if(pchd->ReplyID>=h->ReplyID-1){
								h->ReplyLen=size-sizeof(TCmdHeader);
								UDK_LOGW("->OK, Wait...,ReplyLen=%d\n",h->ReplyLen);
							}else{
								UDK_LOGW( "->Invalid ReplyID: Expect %d But %d \n", h->ReplyID-1, pchd->ReplyID);
								continue;
							}
							m_WorkerState = m_NextWorkerState = kStateSyncCommandServiced;
							commandcond.signal();
						}else{
							UDK_LOGW("Receive timeout in worker thread\n");
							//timeout or invalid command without response,
							//anyway ,just serviced
							m_WorkerState = m_NextWorkerState = kStateSyncCommandServiced;
							commandcond.signal();
						}
					}else if(kStateStopping==m_NextWorkerState){					
						bStopLoop = true;					
						m_WorkerState = m_NextWorkerState = kStateStopped;					
						commandcond.signal();
					}
					
				}else if(true == m_bRegEvent){
					//prepare to send periodic command
					m_WorkerState = m_NextWorkerState = kStatePeriodicEvent;

					//send periodic event
					pchd->Command=CMD_ACK_OK;
					pchd->SessionID=h->SessionID;
					pchd->CheckSum=0;
					//pchd->ReplyID=h->ReplyID; 
					pchd->CheckSum=in_chksum((char *)pchd, sizeof(TCmdHeader));
					h->cSend(h->cHandle, (char *)pchd, sizeof(TCmdHeader));
					//read periodic event	
					size=h->cRead(h->cHandle, recvbuf4periodic, recvbuflength, timeout);
					UDK_LOGD("buffer mode periodic command read size=%d\n",size);
					if(size>0){
						PCmdHeader pchd=(PCmdHeader)recvbuf4periodic;
						int cs = pchd->CheckSum;
						pchd->CheckSum = 0;//to avoid repeated crc?
						//check crc
						if(cs!=in_chksum(h->RecvBuffer, size)){
							UDK_LOGW("->Bad Checksum!!!\n");
						}else if(pchd->Command==CMD_REG_EVENT){
							service_callback(h,pchd,(char*)(pchd+1),size-sizeof(TCmdHeader));

						}else if(pchd->ReplyID>=h->ReplyID-1){
							h->ReplyLen=size-sizeof(TCmdHeader);
							UDK_LOGW("->OK, Wait...\n");
						}else{
							UDK_LOGW( "->Invalid ReplyID: Expect %d But %d \n", h->ReplyID-1, pchd->ReplyID);
							continue;
						}
					}
				}
				break;
			}while(true);
			//got event
			commandmutex.unlock();
			
		}else if(ret<0){
			UDK_LOGW("wait event error %d in buffer mode\n",ret);
			bStopLoop = true;
		}
		return bStopLoop==false;
	}

private:	
	int wait_event(int msto/*timeout in ms*/)
	{
		int err;
		struct pollfd pfds[2];
		
		pfds[0].fd = readerCmdFds[0];
		pfds[0].events = POLLIN;

		err = poll(pfds, 1, msto);
		if (pfds[0].revents & POLLIN) {
		     char buf[10];
		            /* Just drain it. We don't care, this is just for waking up. */
		     int ret = read(pfds[0].fd, &buf, 1);
		}
		return err;
	}


	virtual bool threadLoop(){
		PHandleHeader h = m_ph;		
		if (true==m_bATmode){
			return run_atmode_loop(h);
		}else{ 	
			return run_buffer_mode(h);
		}

		return true;
	}

	void service_callback(PHandleHeader h,PCmdHeader pchd,char* buf,int bufsize)
	{
		static int LastRID=-1;
		if(h->ReadCallBackFunc){
			UDK_LOGD( "ReplyID: %d-%d, Data=%8X\n", LastRID, pchd->ReplyID, *(int*)(pchd+1));
			//henry modify 2007-2-1 for events.
			if(LastRID!=pchd->ReplyID || LastRID==0){
				h->ReadCallBackFunc(h->Param, pchd->SessionID, bufsize, buf);
				LastRID=pchd->ReplyID;
			}
		}
	}
private:	
	PHandleHeader m_ph;
	bool m_bATmode;
	int m_WorkerState;
	int m_NextWorkerState;
	int readerCmdFds[2];	
	int readerClosed;	
    Mutex commandmutex;
	Condition commandcond;
	bool m_bRegEvent;
	char* recvbuf4periodic;
	int recvbuflength;
	int cmdtimeout;
	CCommproThread(){}
};

static int send_at_command(PHandleHeader h,
								char* sendbuffer ,
								int sendlen)
{
  return h->cSendATCmdToModem(h->cHandle, sendbuffer, sendlen);
}
static int send_at_command_full_nolock(PHandleHeader ph,
								char* sendbuffer ,
								int sendlen,
								int timeout_ms)
{
	int err = 0;
	CCommproThread* cct = ph->ReadThread;
	int replyID;

	/* FIXME This is to prevent future problems due to calls from other threads;
	 * should be revised.
	 */
	/* FIXME: This will attempt to wait on ac->requestcond without holding
	 * ac->commandmutex if pthread_mutex_trylock reports that ac->requestmutex
	 * is busy. That is undefeined behaviour according to SUSv2
	 */
	while (ph->requestmutex->tryLock()==EBUSY)
		ph->requestcond->wait(ph->requestmutex);

	if(!ph->PullMode){
		if(CCommproThread::kStateStarted!=cct->get_worker_state()){
			cct->start();
		}
		if(ph->cTerminalRead)
			ph->cTerminalRead(ph->cHandle);
	}
	err = send_at_command(ph,sendbuffer,sendlen);

	ph->atresponse=0;
	if (err <=0)
		goto error;
	if(!ph->PullMode){		
		cct->request_worker_state(CCommproThread::kStateSyncCommand);
		status_t ret = cct->wait_worker_state(CCommproThread::kStateSyncCommandServiced,timeout_ms);
		if(NO_ERROR==ret){
			if(!ph->atresponse){
				err = -ETIMEDOUT;goto error;
			}
		}else{
			UDK_LOGD("wait_worker_state timeout %d\n",ret);
			err = -ETIMEDOUT;goto error;
		}
	}else{
		//pullmode support
		LOG_ALWAYS_FATAL("pullmode of stdsdk not supported\n");		
	}
	//
	err = 0;


error:	
	ph->requestcond->broadcast();
	ph->requestmutex->unlock();

	return err;
}

/**
 * Internal send_command implementation.
 *
 * timeoutMsec == 0 means infinite timeout.
 */
static int send_at_command_full(PHandleHeader ph,
								char* sendbuffer ,
								int sendlen,
								int retries,
								int timeout_ms)
{	
   	int err=0;
	int retry=0;
	while(retry++<retries){
		//assume ReadThread is alive
		ph->ReadThread->lock_worker();
    	err = send_at_command_full_nolock(ph,sendbuffer,sendlen,timeout_ms);
		ph->ReadThread->unlock_worker();
		if(err>=0) break;
	}

    return err;
}

static int send_command(PHandleHeader h,
		unsigned short *Cmd,char *Buf, 
		int BufLen, int SendLen,unsigned short *NextReplyID)
{
	int ret = 0;
	PCmdHeader pchd=(PCmdHeader)h->SendBuffer;
	TCmdHeader tchd;
	
	int packetlen=SendLen+sizeof(TCmdHeader);
	if(h->SendBufferLength<packetlen)	
	{
		UDK_LOGW("buffer size is small expected[%d] allocated[%d]\n",packetlen,h->SendBufferLength);
		*Cmd=CMD_ACK_ERROR_CMD;
		return 0;
	}
	pchd->CheckSum=0;
	pchd->Command=*Cmd;
	//session process
	pchd->ReplyID=h->ReplyID;//last replyid
	*NextReplyID=h->ReplyID+1;//expected rellyid?
	pchd->SessionID=h->SessionID;
	//UDK_LOGD( "Before prepare data h->ReplyID=%d,pchd->ReplyID=%d\n", h->ReplyID,pchd->ReplyID);
	//prepare buffer
	if(SendLen>0){ 
		memcpy(pchd+1,Buf, SendLen);
	}
	pchd->CheckSum=in_chksum((char*)pchd, packetlen);

	memcpy(&tchd,h->SendBuffer,sizeof(TCmdHeader));
	UDK_LOGD( "---------[%d] SendCmd:%s packetlen:%d------------\n", *NextReplyID-1, GetCmdName(pchd->Command),packetlen);
	//prepare to xfer data
	h->ReplyLen=-1;
	//UDK_LOGD( "Before cSend h->ReplyID=%d,pchd->ReplyID=%d\n", h->ReplyID,tchd.ReplyID);
	ret = h->cSend(h->cHandle, (char *)h->SendBuffer, packetlen);
	if(ret <= 0) {
		*Cmd=CMD_ACK_ERROR_CMD;
	}
	//UDK_LOGD("Send packet return =%d\n",ret);
	
	return ret;
}

static int process_response(PHandleHeader h,unsigned short NextReplyID,unsigned short *command,char* buffer,int buflen)
{
	int ret=0;
	PCmdHeader pchd=(PCmdHeader)h->RecvBuffer;
	h->ReplyID=NextReplyID;
	//��
	UDK_LOGD("replylen=%d,replyid=%d\n",h->ReplyLen,h->ReplyID);
	if(h->ReplyLen>=0)
	{
		int clen=h->ReplyLen;
		if(clen>buflen) clen=buflen;
		if(clen>0){
			memset(buffer,0,buflen);
			memcpy(buffer, h->RecvBuffer+sizeof(TCmdHeader), clen);		
		}
		if(pchd->Command==CMD_ACK_REPEAT)
		{
			*command=CMD_ACK_REPEAT;
		}
		else if(pchd->Command==CMD_ACK_RETRY)
		{
			udk_msleep(200);
			ret = -EAGAIN;
		}
		else
			*command=pchd->Command;
		ret=pchd->SessionID;
	}

	return ret;
}

static int send_command_full_nolock(PHandleHeader ph,
								unsigned short *command,
								char* buffer ,
								int buflen,
								int sendlen,								
								int syncmode,
								int timeout_ms,
								int *norep)
{
	int err = 0;
	CCommproThread* cct = ph->ReadThread;
	unsigned short replyID;
	/* FIXME This is to prevent future problems due to calls from other threads;
	 * should be revised.
	 */
	/* FIXME: This will attempt to wait on ac->requestcond without holding
	 * ac->commandmutex if pthread_mutex_trylock reports that ac->requestmutex
	 * is busy. That is undefeined behaviour according to SUSv2
	 */
	while (ph->requestmutex->tryLock()==EBUSY)
		ph->requestcond->wait(ph->requestmutex);

	if(!ph->PullMode){
		if(CCommproThread::kStateStarted>cct->get_worker_state()){			
			cct->start();
		}
		if(ph->cTerminalRead)
			ph->cTerminalRead(ph->cHandle);
	}
	err = send_command(ph,command,buffer,buflen,sendlen,&replyID);
	if (err <=0)
		goto error;
	if(!ph->PullMode&&(true==syncmode)){
		int result = cct->request_worker_state(CCommproThread::kStateSyncCommand);
		status_t ret = cct->wait_worker_state(CCommproThread::kStateSyncCommandServiced,timeout_ms);		
		if(NO_ERROR==ret){
			//return
			err = process_response(ph,replyID,command,buffer,buflen);
		}else{
			UDK_LOGW("wait_worker_state timeout\n");
			if(norep) (*norep)++;
			err = -ETIMEDOUT;goto error;
		}
	}else if(true == ph->PullMode){
		//pullmode support
		LOG_ALWAYS_FATAL("pullmode of stdsdk not supported\n");	
		err = -ENOSYS;goto error;
	}
	//
error:
	
	ph->requestcond->broadcast();
	ph->requestmutex->unlock();

	return err;
}

/**
 * Internal send_command implementation.
 *
 * timeoutMsec == 0 means infinite timeout.
 */
static int send_command_full(PHandleHeader ph,
								unsigned short *command,
								char* buffer ,
								int buflen,
								int sendlen,
								int timeout_ms,
								int *norep)
{	
#define DEFAULT_RETRIES 2
   	int err=0;
	int retry;
	int retries;
	retries=DEFAULT_RETRIES;
	if(buflen>0) retries+=2;
	if(*command==CMD_CONNECT) 
	{
		retries = 1;
	}
	if(CMD_APP_PULL_ATT_RECOREDS == *command || CMD_APP_SET_TOKEN == *command ||
		CMD_APP_QUERY_TOKEN == *command || CMD_APP_WIFI_CHECKIN == *command || 
		CMD_APP_PULL_COMPANY_REPORTS == *command || CMD_APP_PULL_PERSONAL_REPORTS == *command)
	{
		retries = 2;
	}
	else if(*command==CMD_EXIT)
		retries=1;
	if(timeout_ms==0) 
		retries=1;


	retry=0;
	while(retry++<retries){
		//assume ReadThread is alive
		ph->ReadThread->lock_worker();
    	err = send_command_full_nolock(ph,command,buffer,buflen,sendlen,true,timeout_ms,norep);
		ph->ReadThread->unlock_worker();
		if(err>=0) break;
	}
	if(err<0&&retry==retries){
		UDK_LOGW("retry times exhausted\n");
		*command=CMD_ACK_NO;
		err=-2;
	}

    return err;
}

static inline void worker_thread_lock(PHandleHeader ph)
{
	ph->ReadThread->lock_worker();
}
static inline void worker_thread_unlock(PHandleHeader ph)
{
	ph->ReadThread->unlock_worker();
}

static int send_command_full_async(PHandleHeader ph,
								unsigned short *command,
								char* buffer ,
								int buflen,
								int sendlen,
								int timeout_ms,
								int *norep)
{	
#define DEFAULT_RETRIES 2
   	int err=0;
	int retry;
	int retries;
	retries=DEFAULT_RETRIES;
	if(buflen>0) retries+=2;
	if(*command==CMD_CONNECT) 
	{
		retries = 2;
	}
	else if(*command==CMD_EXIT)
		retries=1;
	if(timeout_ms==0) 
		retries=1;


	retry=0;
	while(retry++<retries){
    	err = send_command_full_nolock(ph,command,buffer,buflen,sendlen,false,timeout_ms,norep);
		if(err>=0) break;
	}
	if(err<0&&retry==retries){
		*command=CMD_ACK_NO;
		err=-2;
	}

    return err;
}


int SendATCmdToModem(PHandleHeader header, int ATCommand)
{
	int ret;
	int iSendTimes = 2;
	int iTimeout = 3000;
	char cmdValue[30]={0};
	PHandleHeader h = header;

	switch (ATCommand)
	{
	case ATCMD_AT:
		strcpy(cmdValue, "AT\r");		
		break;
	case ATCMD_ATD:
		sprintf(cmdValue, "ATD%s\r", header->Telephone);
		iTimeout = 30000;
		break;
	case ATCMD_CMDMODE:
		strcpy(cmdValue, "+++\r");
		break;
	case ATCMD_ATH:
		strcpy(cmdValue, "+++ATH\r");
		break;
	default:
		strcpy(cmdValue, "ATH\r");
		break;
	}		
	h->ATCmdLength = strlen(cmdValue);
	h->ATCMD = ATCommand;
	ret = send_at_command_full(h,cmdValue,strlen(cmdValue),iSendTimes,iTimeout);
	if(ret>=0) return UDK_TRUE;

	return UDK_FALSE;
	
}

int ModemDialup(PHandleHeader header)
{
	int result = UDK_FALSE;
	int i = 0;
	PHandleHeader h = header;	
	
	result = SendATCmdToModem(header, ATCMD_AT);	
	if (result)
	{			
		i = GetSeparatorIndex(h->RecvBuffer);
		if (i)
		{
			if (strncmp(h->RecvBuffer+i, "OK", 2) != 0)
			{
				result = UDK_FALSE;
			}
		}
		else
		{
			result = UDK_FALSE;
		}
	}

	if (result)
	{			
		result = SendATCmdToModem(header, ATCMD_ATD);
		if (result)
		{
			i = GetSeparatorIndex(h->RecvBuffer);
			if (i)
			{
				if (strncmp(h->RecvBuffer+i, "CONNECT", 7) != 0)				
				//if (strncmp(h->RecvBuffer+i, "NO", 2) != 0)				
				{
					result = UDK_FALSE;
				}
			}
			else
			{
				result = UDK_FALSE;
			}
		}
	}	
	UDK_LOGD("---ModemDialup result = %d\n", result);
	return result;
}

static void init_commpro_object(PHandleHeader h)
{
  char* p = (char*)h;
  memset(p,0,sizeof(struct _HandleHeader));
  h->requestmutex = new Mutex();
  h->requestcond = new Condition();
}

//use special communication library to connect with device, and initialization the protocol
 UDK_HANDLE ZEMBPRO_INIT(const char *CommLib,const char *Address, unsigned short Port, int Speed, ReadCallBack ReadFun, void* Param, int CommKey)
{
	PHandleHeader h;
	int i = 0;
	unsigned short cmd;
	UDK_U32 tid;	
	int timeOut = 10000;
	int timeOutInit = timeOut;
	int TmpCommKey = CommKey; //zhc add TmpCommKey 2010-06-01, �ı�ȡ�β�CommKey�ĵ�ַ��Ϊ����ݣ�������߳�ʹ��ʱ�������ڴ���ʳ��?
	char szConnFlag[256] = {0x0};
	h=(PHandleHeader)udk_malloc(sizeof(THandleHeader)); if(!h) return NULL;
	init_commpro_object(h);
	int ret = comm_module_interface_init(h,CommLib);
	if(ret){
		LastError = ret;
		udk_free(h); 
		return 0;
	}
	h->slOptionCache = slCreate("=");
	if(strncmp(h->cName(h),"TCP",3)==0){
		h->RecvBuffer = (char*)udk_malloc(MAX_BUFFER_LEN_TCP);
		h->RecvBufferLength = MAX_BUFFER_LEN_TCP;
		h->SendBuffer = (char*)udk_malloc(MAX_BUFFER_LEN_TCP);
		h->SendBufferLength = MAX_BUFFER_LEN_TCP;
		timeOutInit = 1000;
	}else if(strncmp(h->cName(h), "USBSTD", 6) == 0){
		h->RecvBuffer = (char*)udk_malloc(MAX_BUFFER_LEN*3);
		h->RecvBufferLength = 512;
		h->SendBuffer = (char*)udk_malloc(MAX_BUFFER_LEN);
		h->SendBufferLength = 512;		
	}else{
		h->RecvBuffer = (char*)udk_malloc(MAX_BUFFER_LEN);
		h->RecvBufferLength = MAX_BUFFER_LEN;
		h->SendBuffer = (char*)udk_malloc(MAX_BUFFER_LEN);
		h->SendBufferLength = MAX_BUFFER_LEN;		
	}
	h->ReadCallBackFunc=ReadFun;
	if(h->ReadCallBackFunc)//zl?
		h->PullMode = UDK_FALSE;
	else
		h->PullMode = UDK_TRUE;
	
	h->commparams.PackageSize = 5*1024;
	h->commparams.Quirks|=COMM_PARAM_QUIRK_DEBUG;
	h->commparams.StartTag = PROTO_START_TAG;
	h->commparams.EndTag = PROTO_END_TAG;
	h->commparams.DestAddr = Port;
	h->commparams.TimeOutMS = timeOutInit;
	h->cHandle=h->cInit(&h->commparams,Address, Port, Speed);	
	if(!h->cHandle)
	{			
		LastError = ZKEMSDK_ERR_INIT;
		goto init_failed;	
	}
	
	h->Param=Param;
	h->SessionID=0;
	h->ReplyID=0;
	h->DataDelay=0;
	h->BlockSize=0;


	if(!h->PullMode){
		h->ReadThread=new CCommproThread(h);
	}
	cmd=CMD_CONNECT;
	if(h->ReadThread || h->PullMode)
	{
		sprintf(szConnFlag, "client=phone");
		LastError = -4;
		//dsl
		if (h->Dialup)
		{
			if (ModemDialup(h))
			{
				//MODEM��ͨ��,��绰Ϊ��.
				memset(h->Telephone, 0, sizeof(h->Telephone));
				i=ZEMBPRO_SENDCMD(h, &cmd, szConnFlag, 0, strlen(szConnFlag)+1, 9000);
			}
		}
		else
		{
			i = ZEMBPRO_SENDCMD(h, &cmd, szConnFlag, 0, strlen(szConnFlag)+1, timeOut);
		}		
        UDK_LOGD("returned cmd: %d\n", cmd);
		if(cmd==CMD_ACK_OK)
		{
			h->SessionID=i;
			h->ReplyID=1;
		}
		else if(cmd==CMD_ACK_UNAUTH)
		{
			LastError = ZKEMSDK_ERR_AUTH;
			h->SessionID=i;
			h->ReplyID=1;
			cmd=CMD_AUTH;
//			CommKey=789456; //Test Only
			CommKey=MakeKey(CommKey, h->SessionID);

			TmpCommKey = CommKey; //zhc add 2010-06-01
			i = ZEMBPRO_SENDCMD(h, &cmd, (char*)&TmpCommKey, 4, 4, 5000);
		}		
		else
		{
			if (h->Dialup)
			{
				LastError = ATCMD_ERR;
			}
			else
			{
				LastError = ZKEMSDK_ERR_REPLY;
			}
		}
		//UDP���ͳ�ʱ
		if (-2 == i)
		{
			LastError = ZKEMSDK_ERR_TIMEOUT;
		}
	}
	else
	{
		LastError = ZKEMSDK_ERR_READ_MODE;
	}

	if(cmd!=CMD_ACK_OK)
	{
        if (cmd != CMD_ACK_UNAUTH)
            LastError = ZKEMSDK_ERR_READ_MODE;
		goto init_failed;
	}

	LastError = ZKEMSDK_ERR_SUCC;
	return h;
init_failed:	
	if(h->ReadThread) {delete h->ReadThread;h->ReadThread=NULL;}
	if(h->cHandle){h->cClose(h->cHandle);h->cHandle=NULL;}
	if(h->cLibHandle) {udk_dlclose(h->cLibHandle);h->cLibHandle=NULL;}
	if(h->RecvBuffer) {udk_free(h->RecvBuffer);h->RecvBuffer = NULL;}
	if(h->SendBuffer) {udk_free(h->SendBuffer);h->SendBuffer = NULL;}
	if(h->slOptionCache){slFree(h->slOptionCache);h->slOptionCache=NULL;}
	if(h->requestmutex) {delete h->requestmutex;h->requestmutex = NULL;}
	if(h->requestcond) {delete h->requestcond;h->requestcond = NULL;}
	udk_free(h);
	return NULL;
}

//close the connection and free resources
 void ZEMBPRO_CLOSE(UDK_HANDLE Handle)
{
	unsigned short cmd=CMD_EXIT;
	PHandleHeader h=((PHandleHeader)Handle);
	if(!h) return;
	
	ZEMBPRO_SENDCMD(Handle, &cmd, NULL, 0,0 , 5000);
	//dsl
	if (h->Dialup)
	{
		SendATCmdToModem(h, ATCMD_ATH);		
	}
	if(h->cTerminalRead){h->cTerminalRead(h->cHandle);}
	if(h->ReadThread) {delete h->ReadThread;h->ReadThread=NULL;}
	if(h->cHandle){h->cClose(h->cHandle);h->cHandle=NULL;}
	if(h->cLibHandle){udk_dlclose(h->cLibHandle);h->cLibHandle=NULL;}	
	if(h->SendBuffer){udk_free(h->SendBuffer);h->SendBuffer=NULL;}
	if(h->RecvBuffer){udk_free(h->RecvBuffer);h->RecvBuffer=NULL;}
	if(h->slOptionCache){slFree(h->slOptionCache);h->slOptionCache=NULL;}	
	if(h->requestmutex) {delete h->requestmutex;h->requestmutex = NULL;}
	if(h->requestcond) {delete h->requestcond;h->requestcond = NULL;}
    if (h->sFwVersion) 
        udk_free(h->sFwVersion);
	udk_free(Handle);
}

static const char* optionnames[]=
{
	"~OS",
	"ExtendFmt",
	"~ExtendFmt",
	"ExtendOPLog",
	"~ExtendOPLog",
	"~Platform",
	"~ZKFPVersion",
	"WorkCode",
	"~SSR",
	"~PIN2Width",
	"~UserExtFmt",
	"BuildVersion",
	"AttPhotoForSDK",
	"~IsOnlyRFMachine",
	"CameraOpen",
	"CompatOldFirmware",
	"IsSupportPull",
	"Language",
	"~SerialNumber",
	"FaceFunOn",
	"~DeviceName",
};
static int construct_option_query_buf(char* buf)
{
	int i;
	int s = sizeof(optionnames)/sizeof(optionnames[0]);
	int len=0;
	for(i=0;i<s;i++){
		if(i==s-1)
			len+=sprintf(buf+len,"%s=?",optionnames[i]);
		else
			len+=sprintf(buf+len,"%s=?,",optionnames[i]);
	}
	return len;
}
static int search_option_name(char* opname)
{
	int i;
	int s = sizeof(optionnames)/sizeof(optionnames[0]);	
	for(i=0;i<s;i++){
		if(!strcmp(opname,optionnames[i])) return i;
	}
	return -1;
}

static int ReadOptionFromCache(UDK_HANDLE Handle,unsigned short* cmd,char* userbuf,int buflen)
{
#ifdef _PC
  PHandleHeader h = (PHandleHeader)Handle;
  char* nvname,*nvvalue;

  if (h->RRQProbed && !h->SupportRRQExtend) {
    return -1;
  }
  //check if sl is null
  if(0==h->slOptionCache->allCount){
    char* buf = (char*)udk_malloc(8192);
    if(buf){
      int length = 8192;
	  int sendlen=0;
      unsigned short command = CMD_OPTIONS_RRQ_EXTEND;
      memset(buf,0,length);
	sendlen = construct_option_query_buf(buf);
      ZEMBPRO_SENDCMD(h,&command,buf,length,sendlen,10000);
      if(command==CMD_ACK_OK){
        //update local cache
        slSetText(h->slOptionCache,buf,",");
        h->SupportRRQExtend++;
      } 
      h->RRQProbed = 1;
      //free buffer
      udk_free(buf);
    }
  }
  if(!userbuf) return -1;
  nvname = strdup(userbuf);
  nvvalue = slGetValue(h->slOptionCache,nvname); 
  if(NULL!=nvvalue){	
	sprintf(userbuf,"%s=",nvname);
    strcat(userbuf,nvvalue);
    *cmd = CMD_ACK_OK;
	free(nvname);
    return 0;
  }else if(h->SupportRRQExtend&&search_option_name(nvname)>=0){
	  *cmd = CMD_ACK_ERROR;
	  free(nvname);
	  return 0;
  }
  free(nvname);
  #endif
  return -1;
}

static int WriteOptionToCache(UDK_HANDLE Handle,unsigned short* cmd,char* userbuf,int buflen)
{
#ifdef _PC
  PHandleHeader h = (PHandleHeader)Handle;
  if (!h->SupportRRQExtend) {
    return -1;
  }
  //check if sl is null
  if(0==h->slOptionCache->allCount){
    char* buf = (char*)udk_malloc(8192);
    if(buf){
      int length = 8192;
	  int sendlen=0;
      unsigned short command = CMD_OPTIONS_RRQ_EXTEND;
      memset(buf,0,length);
	sendlen = construct_option_query_buf(buf);
      ZEMBPRO_SENDCMD(h,&command,buf,length,sendlen,10000);
      if(command==CMD_ACK_OK){
	//update local cache
	slSetText(h->slOptionCache,buf,",");
      }
      //free buffer
      udk_free(buf);
    }
  }
    {
      char* name,*value;
      char* token=strstr(userbuf, h->slOptionCache->valueSeparator);      
	name = value = NULL;
      if(token){
	*token = '\0';
	name = userbuf;
	value = ++token;	
      }
      //does value support null???
      if(!name&&strlen(name))
	slSetValue(h->slOptionCache,name,value);
    }
    return 0;//it seems nobody care about our return value  
    #else
		return -1;
	#endif  
}

int ZEMBPRO_SENDCMD(UDK_HANDLE Handle, unsigned short *Cmd, char *Buf, int BufLen, int SendLen, int TimeOutMS)
{
	PHandleHeader h = (PHandleHeader)Handle;
	int ret,c=0, rep=(TimeOutMS==0)?1:2;
	unsigned short cmd=*Cmd;
	int needUpdateCache=0;
	char* OptionStringPair=NULL;
    ret = 0;
	if(CMD_CONNECT==cmd || CMD_APP_PULL_ATT_RECOREDS == cmd || CMD_APP_SET_TOKEN == cmd ||
		CMD_APP_QUERY_TOKEN == cmd || CMD_APP_WIFI_CHECKIN == cmd || 
		CMD_APP_PULL_COMPANY_REPORTS == cmd || CMD_APP_PULL_PERSONAL_REPORTS == cmd)
	{
		rep = 1;
	}
	//check cache
	if(h->lastCommand==CMD_GET_FREE_SIZES&&CMD_GET_FREE_SIZES==cmd){
		memcpy(Buf,h->aFreeSize,50*sizeof(int));
		*Cmd = CMD_ACK_OK;
		return 0;
	}
	if(CMD_OPTIONS_RRQ==cmd){
	  ret = ReadOptionFromCache(Handle,Cmd,Buf,BufLen);
	  if(ret > 0) {
		  return 0;
	  }else if(!ret){
		  return 0;
	  }	
	}else if(CMD_OPTIONS_WRQ==cmd){
	  needUpdateCache++;
	  OptionStringPair = strdup(Buf);
	}else if(CMD_GET_VERSION==cmd&&h->sFwVersion){
		strcpy(Buf,h->sFwVersion);
		*Cmd = CMD_ACK_OK;
		return 0;
	}
	while(c++<rep)
	{
		cmd=*Cmd;
		UDK_LOGD(" Request =%s:%u BufLen=%d SendLen:%d timeout:%dms\n",
						GetCmdName(*Cmd),*Cmd,
						BufLen,SendLen,TimeOutMS);
		ret=send_command_full((PHandleHeader)Handle, &cmd, Buf, BufLen, SendLen, TimeOutMS, NULL);
		UDK_LOGD(" Response=%s:%u ret=%d\n",
						GetCmdName(cmd),cmd,ret);
		if(*Cmd==CMD_APPEND_USER || *Cmd==CMD_APPEND_USERTEMP || (*Cmd<=CMD_RESUME && *Cmd>CMD_CONNECT)) break;
		if(cmd==CMD_ACK_REPEAT && (BufLen==0 || Buf==NULL)) 
		{
			cmd=CMD_ACK_OK;
			break;
		}
		if(cmd!=CMD_ACK_NO && cmd!=CMD_ACK_ERROR_DATA && cmd!=CMD_ACK_ERROR_CMD && cmd!=CMD_ACK_REPEAT) break;
		if(((*Cmd==CMD_EXIT) | (*Cmd==CMD_CONNECT)) && (c>0)) break;
		udk_msleep(100);
		
	}
	h->lastCommand = *Cmd;
	if(CMD_GET_VERSION==*Cmd&&CMD_ACK_OK==cmd){
		if(!h->sFwVersion)
			h->sFwVersion = strdup(Buf);
	}else if(CMD_GET_FREE_SIZES==*Cmd&&CMD_ACK_OK==cmd){
		memcpy(h->aFreeSize,Buf,50*sizeof(int));
	}
	if(*Cmd==CMD_EXIT) 
		*Cmd=CMD_ACK_OK;
	else
		*Cmd=cmd;
	if(needUpdateCache&&CMD_ACK_OK==cmd){
	  WriteOptionToCache(Handle,&cmd,OptionStringPair,strlen(OptionStringPair));
	  free(OptionStringPair);	  
	}
	return ret;
}

//send a data buffer to device
 UDK_BOOL ZEMBPRO_SENDDATA(UDK_HANDLE Handle, char *Buf, int BufLen, int TimeOutMS)
{
	unsigned short cmd=CMD_PREPARE_DATA;
	int rep, len=BufLen, StartMS=udk_get_ticks(),i=0;
	char buffer[16];
	*(int*)buffer=len;
	/*if(((PHandleHeader)Handle)->DataDelay==0) ((PHandleHeader)Handle)->DataDelay=TimeOutMS;*/
	((PHandleHeader)Handle)->DataDelay=TimeOutMS;
	send_command_full((PHandleHeader)Handle, &cmd, buffer, 16, 4, TimeOutMS, NULL);
	if(cmd==CMD_ACK_OK)
	while(BufLen>0)
	{
		len=((PHandleHeader)Handle)->RecvBufferLength -sizeof(TCmdHeader);
		//len=MAX_BUFFER_LEN-sizeof(TCmdHeader);
		if(len>BufLen) len=BufLen;
		rep=5;
		while(rep--)
		{
			int res=0;
			cmd=CMD_DATA;
			send_command_full((PHandleHeader)Handle, &cmd, Buf, 0, len, ((PHandleHeader)Handle)->DataDelay, &res);
			if(res>0)
			{
				((PHandleHeader)Handle)->DataDelay+=500;
				UDK_LOGI("\t\t%d: DataDelay->%d\n", i, ((PHandleHeader)Handle)->DataDelay);
				if(((PHandleHeader)Handle)->DataDelay>=5000 && cmd==CMD_ACK_NO) 
				{
					rep=-1;
					break;
				}
			}
			if(cmd==CMD_ACK_DATA || cmd==CMD_ACK_OK || cmd==CMD_ACK_REPEAT) 
			{
				if(i++%10==0)//���¼��㷢��һ����ݰ����ʱʱ��
				{
					((PHandleHeader)Handle)->DataDelay=(((PHandleHeader)Handle)->DataDelay+(300+(udk_get_ticks()-StartMS)/i))/2;
					if (0 == strncmp(((PHandleHeader)Handle)->cName(Handle),"RS232",5))
					{
						if(((PHandleHeader)Handle)->DataDelay<TimeOutMS) ((PHandleHeader)Handle)->DataDelay=TimeOutMS;
					}
					else
					{
						if(((PHandleHeader)Handle)->DataDelay<150) ((PHandleHeader)Handle)->DataDelay=150;
					}
					UDK_LOGI("\t\t%d: DataDelay->%d\n", i, ((PHandleHeader)Handle)->DataDelay);

					//WriteDbgLog("\t\tDataDelay->%d\n",((PHandleHeader)Handle)->DataDelay);
				}
				cmd=CMD_ACK_OK;
				break;
			}
			if(rep) 
			{
				((PHandleHeader)Handle)->ReplyID--;
				//WriteDbgLog("\t\ReplyID-- %d\n",((PHandleHeader)Handle)->ReplyID);
			}
		}
		if(rep<0) break;
		Buf+=len;
		BufLen-=len;
	}
	return BufLen==0;
}

//send a special data buffer to device
 int ZEMBPRO_SENDCMDDATA(UDK_HANDLE Handle, 
	unsigned short *Cmd, char *Buf, int BufLen, 
	char *CmdBuf, int SendLen, int TimeOutMS)
{
	int ret;
#ifndef COMM_TCP
	unsigned int hash=hashpjw(Buf, BufLen);
#endif
	if(ZEMBPRO_SENDDATA(Handle, Buf, BufLen, TimeOutMS))
	{
		unsigned short cmd=CMD_HASH_DATA;
		unsigned int Hash=0;
#ifndef COMM_TCP
		ret=ZEMBPRO_SENDCMD(Handle, &cmd, (char*)&Hash, 4, 0, TimeOutMS);
		if((cmd==CMD_ACK_OK && Hash==hash)|| cmd==CMD_ACK_UNKNOWN)
		{
#endif
			ret=ZEMBPRO_SENDCMD(Handle, Cmd, CmdBuf, 0, SendLen, TimeOutMS*4);
			if(*Cmd==CMD_ACK_OK || *Cmd==CMD_ACK_REPEAT)
			{
				*Cmd=CMD_FREE_DATA;
				ret=ZEMBPRO_SENDCMD(Handle, Cmd, NULL, 0, 0, 3000);
				if(*Cmd!=CMD_ACK_OK)
				{
					*Cmd=CMD_FREE_DATA;
					ret=ZEMBPRO_SENDCMD(Handle, Cmd, NULL, 0, 0, 3000);
				}
				*Cmd=CMD_ACK_OK;
			}
#ifndef COMM_TCP
		}
		else
			ret=0;
#endif
		return ret;
	}
	else
		*Cmd=CMD_ACK_NO;
	return 0;
}

int CheckFullData(unsigned char *flags, unsigned int size)
{
	if(flags)
	while(size--) 
		if(!*flags++) 
			return 0;
	return 1;
}

int CheckFullData2(unsigned char *flags, unsigned char *flags2, unsigned int size)
{
	if(!flags2)
		return CheckFullData(flags, size);
	if(flags)
	while(size--)
	{
		if(!*flags && !*flags2)
				return 0;
		flags++;
		flags2++;
	} 
	return 1;
}

//read data from device to buffer
static int READDATA(UDK_HANDLE Handle, unsigned short *Cmd, char *SendBuf, int SendLen, char *Buf, 
	int BufLen, UDK_BOOL CheckData, int TimeOutMS, int *RecLen, char *BFlags)
{
	int p, len=0, psize, reply, rlen, clen, res=0, pcount, pcc, repc, HasHeader=UDK_FALSE;
	unsigned short cs;
	unsigned char  *Flags=NULL ;//buffer[MAX_BUFFER_LEN];
	PHandleHeader h=(PHandleHeader)Handle;
	char *buffer=h->RecvBuffer;
	PCmdHeader pchd=(PCmdHeader)buffer;
	int testtemp = 0;
	unsigned short cmdorg = *Cmd;
    pcount = 0;
	//worker_thread_lock((PHandleHeader)Handle);
	int ret=send_command_full((PHandleHeader)Handle, Cmd, SendBuf, MAX_BUFFER_LEN, SendLen, TimeOutMS, NULL);
	if(*Cmd==CMD_PREPARE_DATA)
	{
		len=*(int*)SendBuf;//Ҫ������ݵĴ�С
		psize=((int*)SendBuf)[1];//ÿ����Ĵ�С
		pcount=(len+psize-1)/psize;
		if(*RecLen && len!=*RecLen)
		{
			*RecLen=0; 
			if(BFlags)
				memset(BFlags, 0, pcount);
			UDK_LOGW( "Dirty Data, Len=%d.\n", len);
		} 
		if(CheckData)
		{
			Flags=(unsigned char*)udk_malloc(pcount);
			memset(Flags,0,pcount);
		}
		if(BufLen<len)
		{
			*Cmd=CMD_ACK_ERROR_DATA;
			while(h->cRead(h->cHandle, buffer, MAX_BUFFER_LEN, 200));			
			//worker_thread_unlock((PHandleHeader)Handle);
			UDK_LOGD( "Buffer size(%d) too small, expect %d\n", BufLen, len);
			return 0;
		}

		TimeOutMS/=3;
		pcc=0;
		repc=0;
		reply=h->ReplyID-1;
		while(pcc<pcount+1) ////��1�����⻺�����л�����û�д�������(�̼��ж෢�� CMD_ACK_OK)  zhc 2010-05-19
		{
			rlen=h->cRead(h->cHandle, buffer, h->RecvBufferLength, TimeOutMS);
			if(rlen)
			{
				if(pchd->ReplyID==reply)
				{
					if(pchd->SessionID==0) HasHeader=UDK_TRUE;
					p=pchd->SessionID*psize;
					clen=rlen-sizeof(TCmdHeader);
					if(p+clen>(len+8))
					{
						*Cmd=CMD_ACK_ERROR_DATA;
						res= -3;
						UDK_LOGW( "Invalid data length\n");
						break;
					}
					else
					{
//�˴������˼������ǰ��ȡ����ݵ�һ����
/*						if(len==*RecLen && BFlags && BFlags[pchd->SessionID])	//�Ѿ�����һ�����ݣ�
						{														//����Ӧ������Ƿ�һ�£�
							if(memcmp(pchd+1, Buf+p, clen))						//��һ�µģ�
							{													//ȡ���Ѷ�ȡ״̬
								memcpy(Buf+p,pchd+1,clen);
								memset(BFlags, 0, pcount);
								*RecLen=0;
								UDK_LOGW "Dirty Data at %d.\n", p);
							}
						}
						else */
							memcpy(Buf+p,pchd+1,clen);
							UDK_LOGW( "  %d", p);
							if(p==9216)
								p=9216;
					}
					if(CheckData)
					{
//�˴����У���
						cs=pchd->CheckSum;
						pchd->CheckSum=0;
						if(clen<=BufLen && cs==in_chksum(buffer, rlen))
						{
							Flags[pchd->SessionID]=1;
//							UDK_LOGD( "Read Data %d-%d OK\n", pcc,pchd->SessionID);
							pcc++; 
						}
						else
						{
							*Cmd=CMD_ACK_ERROR_DATA;
							res= -2;
							UDK_LOGW( "Invalid data\n");
							break;
						}
					}
					else
						pcc++;
					if(pchd->SessionID+1==pcount)	//�Ѷ������һ����ݰ�
					{
						if(!CheckData || CheckFullData2(Flags, (unsigned char*)BFlags, pcount))
						{
							break;
						}
					}
				}
				else if(pchd->ReplyID<reply) 
				{
					continue;
				}
			}
			else if(CheckFullData2(Flags, (unsigned char*)BFlags, pcount))
			{
				res=len;
				*Cmd=CMD_ACK_OK;
				break;
			}
			else
			{
				*Cmd=CMD_ACK_ERROR_DATA;
				res= -4;
				UDK_LOGD( "Invalid data(size=%d, but %d)\n", len, pcc*psize);
				break;
			}
		}
		if(!CheckData && pcc && HasHeader)
		{
			*Cmd=CMD_ACK_OK;
			res= len;
		}
		else if(CheckData && ((res==len) || CheckFullData2(Flags, (unsigned char*)BFlags, pcount)))
		{
			//�˴��ر�ע�⣬��libusb����Ӧ2013-09-13 by zxz
			if(strncmp(h->cName(h), "USBSTD", 6) != 0)
			{
				//rlen=h->cRead(h->cHandle, buffer, MAX_BUFFER_LEN, 200);
				rlen = 0;
				do
				{
					rlen=h->cRead(h->cHandle, buffer, MAX_BUFFER_LEN, 200);
					if (0 >= rlen)
					{
						break;
					}
					pchd = (PCmdHeader)buffer;
					if (pchd->Command == cmdorg)
					{
						break;
					}
				}while(repc++ < 5);
			}
			*Cmd=CMD_ACK_OK;
			res= len;
		}
		else
		{
			*Cmd=CMD_ACK_ERROR_DATA;
			if(res>=0) 
				res= -5;
		}
	}
	else if(*Cmd==CMD_DATA) //CMD_PREPARE_DATA have lost or a single data frame
	{
		if(BufLen-1024==h->ReplyLen) 
		{
			
			memcpy(Buf, h->RecvBuffer+sizeof(TCmdHeader), h->ReplyLen);
			BFlags[0]=1;
			len=h->ReplyLen;
			res=len;
			*Cmd=CMD_ACK_OK;
		}
		else
		{
			UDK_LOGD( "CMD_PREPARE_DATA have lost\n");
			while(h->cRead(h->cHandle, buffer, MAX_BUFFER_LEN, 100)>0) 
				udk_msleep(1); //read all data
		}
	}
	else if(*Cmd!=CMD_ACK_ERROR)
	{
		*Cmd=CMD_ACK_NO;
	}
	if(Flags && ((len==*RecLen) || (*RecLen==0)) && BFlags && (res!=len))
	{
		for(p=0;p<pcount;p++)
			if(Flags[p]) 
				BFlags[p]=1;

		if(CheckFullData((unsigned char*)BFlags, pcount))
		{
			*Cmd=CMD_ACK_OK;
			res=len;
		}
		else
			*RecLen=len;
	}
	if(Flags) 
		udk_free(Flags);
	//if(res!=len)
	//���´���ǳ���Ҫ���벻Ҫɾ��,�̼���������ݷ���֮�󻹻�෢һ�������´�����Ǵ��������
	/*while((testtemp =h->cRead(h->cHandle, buffer, MAX_BUFFER_LEN, 200)))
		UDK_LOGE "-----last cmd from device:%d\n",testtemp);*/
	//worker_thread_unlock((PHandleHeader)Handle);
	return res;
}

 int ZEMBPRO_READDATA1(UDK_HANDLE Handle, unsigned short *Cmd, char *Buf, int BufLen, int SendLen, UDK_BOOL CheckData, int TimeOutMS)
{
	int ret=0,rep=1;
	UDK_U16 cmd;
	char Buffer[4096],TempBuffer[1024] ,BlockFlag[2*1024];
	int RecLen=0;
	///////////////////////////////////////////
	//char strbuf[250] = {0};
	//FILE* fp = NULL;
	//fp = fopen("d:\\ZEMBPRO_READDATA1.txt","a+");

	
	///////////////////////////////////////////
	if(SendLen)
	{
		memcpy(Buffer, Buf, SendLen);
		memcpy(TempBuffer, Buf, SendLen);
	}
	if(CheckData)
	{
		rep=6;//tt
		memset(BlockFlag, 0, 2*1024);
	}
    cmd = CMD_ACK_ERROR;
	while(rep>0)
	{
			cmd=*Cmd;
			if(SendLen)
				memcpy(Buffer, TempBuffer, SendLen); //Buffer�ѱ��޸�,����ʱ����ԭֵ
			ret=READDATA(Handle, &cmd, Buffer, SendLen, Buf, BufLen, CheckData, TimeOutMS, &RecLen, BlockFlag);
			if(cmd==CMD_ACK_ERROR) 
			{
				break;
			}
			if(ret>=0 && cmd==CMD_ACK_OK) 
			{
				break;
			}
			rep--;
	}

	if(rep<=0 && cmd==CMD_ACK_OK && CheckData)
		*Cmd=CMD_ACK_NO;
	else
		*Cmd=cmd;

	/*if(fp) {
		memset(strbuf, 0, sizeof(strbuf));
		sprintf(strbuf, "\n ZEMBPRO_READDATA1() __%s-%d-Cmd:%d-return rep:%d", __FILE__, __LINE__,*Cmd,rep);
		fwrite(strbuf, 1, strlen(strbuf), fp);
		fclose(fp);
	}*/
	return ret;
}

#define MinBlockSize (1024*1)

//Last Error
 int ZEMBPRO_LASTERROR(void)
{
	return LastError;
}

int ReadBlock(UDK_HANDLE Handle, UDK_U16 DataMemory, UDK_U32 Address, UDK_U32 Size, int TryC, char *Buf)
{


	UDK_U16 cmd;
	///////////////////////////////////////////////////
	char strbuf[250] = {0};
	FILE* fp = NULL;
	/*fp = fopen("d:\\rsagentlog.txt","a+");

	if(fp) {
		memset(strbuf, 0, sizeof(strbuf));
		sprintf(strbuf, "\n ReadBlock() __%s-%d--TryC:%d", __FILE__, __LINE__,TryC);
		fwrite(strbuf, 1, strlen(strbuf), fp);
	}*/
	while(TryC--)
	{
		((int*)Buf)[0]=Address;
		((int*)Buf)[1]=Size;
		cmd=DataMemory;
		ZEMBPRO_READDATA1(Handle, &cmd, Buf, Size+1024, 8, UDK_TRUE, 4000*8);
		if(cmd==CMD_ACK_OK) 
		{
			/*if(fp) {
				memset(strbuf, 0, sizeof(strbuf));
				sprintf(strbuf, "\n ReadBlock() cmd==CMD_ACK_OK: %d_-%d--return:%s", cmd, __LINE__,"0");
				fwrite(strbuf, 1, strlen(strbuf), fp);
				fclose(fp);
			}*/
			return 0;
		}
		if((cmd!=CMD_ACK_ERROR_DATA) && (cmd!=CMD_DATA) && (cmd!=CMD_ACK_ERROR_CMD))
		{
			UDK_LOGW("Error of ReadData: %d\n", cmd);
			return -2;
		}
	}

	return -1;
}

 int ZEMBPRO_READMEMORY(UDK_HANDLE Handle, UDK_U16 DataMemory, UDK_U32 StartAddress, int Size, int BlockSize, int TryCount, char *Buffer)
{
	int r, cc=10, rc=0;
	PHandleHeader h=((PHandleHeader)Handle);
	if(h->BlockSize==0) h->BlockSize=BlockSize;
	if(BlockSize>h->BlockSize)
		BlockSize=(h->BlockSize+BlockSize)/2;
	while(Size)
	{
		int ret;
		r=BlockSize;
		if(r>Size)	r=Size;
		ret=ReadBlock(Handle, DataMemory, StartAddress, r, TryCount, Buffer);
		if(0!=ret)
		{
			if(ret==-2)
			{
				return -2;
			}
			BlockSize/=8;
			if(BlockSize<MinBlockSize) 
			{
				if(0==cc--)
				{
					return -3;
				}
				BlockSize=MinBlockSize;
			}
		}
		else
		{
			Size-=r;
			StartAddress+=r;
			Buffer+=r;
			h->BlockSize=BlockSize;
			cc=10;
			rc+=r;
			if(NULL != h->pCBDownloadProcess)
			{
				h->pCBDownloadProcess(1, rc);
			}
		}
	}
	return rc;
}

 int ZEMBPRO_READDATA2(UDK_HANDLE Handle, unsigned short *Cmd, char *Buf, int BufLen, int SendLen, int blockSize, int TimeOutMS)
{
	int ret, rep=5, dataLen;
	unsigned int hash, oriLen;
	int hashtmp = 0;
	int compressed=0;
	char Buffer[1048],TempBuffer[1024];
	int RecLen=0;
	char *dataBuffer;
	PHandleHeader h=((PHandleHeader)Handle);
	memset(Buffer, 0, 1048);
	Buffer[0]=1;//compress data
	*(UDK_U16*)(Buffer+1)=*Cmd;
	if(SendLen)
	{
		if(*Cmd != CMD_APP_PULL_COMPANY_REPORTS && *Cmd != CMD_APP_PULL_PERSONAL_REPORTS &&
			*Cmd != CMD_APP_PULL_ATT_RECOREDS)
		{
			if(SendLen>8) SendLen=8;
		}
		
		memcpy(Buffer+3, Buf, SendLen);
		memcpy(TempBuffer+3, Buf, SendLen);
	}

	*Cmd=CMD_QUERY_DATA;
	//worker_thread_lock((PHandleHeader)Handle);
	//change by zxz for x20
	if(strncmp(h->cName(h), "USBSTD", 6) == 0)
		ret=send_command_full((PHandleHeader)Handle, Cmd, Buffer, 1048, SendLen<=8?11:SendLen+3, 4+TimeOutMS + 100000,NULL);
	else
		ret=send_command_full((PHandleHeader)Handle, Cmd, Buffer, 1048, SendLen<=8?11:SendLen+3, 4+TimeOutMS, NULL);
	if(CMD_DATA==*Cmd){
		PHandleHeader h=(PHandleHeader)Handle;
		*Cmd=CMD_ACK_OK;
		ret=h->ReplyLen;
		memcpy(Buf, h->RecvBuffer+sizeof(TCmdHeader), ret);		
		//worker_thread_unlock((PHandleHeader)Handle);
		return ret;
	}else if(CMD_ACK_OK!=*Cmd){		
		//worker_thread_unlock((PHandleHeader)Handle);
		return -1;
	}
	

	compressed=Buffer[0];
	dataLen=*(int *)(Buffer+1);
	oriLen=*(int *)(Buffer+1+4);
	hash=*(int *)(Buffer+1+8);

	if(oriLen<=0 || dataLen<=0) return 0;
	/*if(BufLen<oriLen)
	{
		*Cmd=CMD_FREE_DATA;
		ret=send_command_full((PHandleHeader)Handle, Cmd, Buffer, 1024, 0, TimeOutMS, NULL);
		//worker_thread_unlock((PHandleHeader)Handle);
		return -100;
	}*/

	if(compressed)
		dataBuffer=(char*)udk_malloc(dataLen);
	else
		dataBuffer=Buf;
	//READMEMORY will hold worker thread lock again ,so we release it here
	//worker_thread_unlock((PHandleHeader)Handle);

	if(NULL != h->pCBDownloadProcess)
	{
		h->pCBDownloadProcess(0, oriLen);
	}
	while(rep--)
	{
		ret=ZEMBPRO_READMEMORY(Handle, CMD_READ_DATA, 0, dataLen, blockSize, 5, dataBuffer);
		if(ret<=0) 
			break; 
		if(ret>0)
		{
			int datalength = 0;
			memcpy(&datalength, dataBuffer, sizeof(int));
			hashtmp = hashpjw(dataBuffer, dataLen);
			if(hashtmp==hash)
				break;
			else
			{
				ret=0;
				UDK_LOGE("ZEMBPRO_READDATA2 recv hash=%d, data hash=%d\n", hash, hashtmp);
			}
		}
	}

	//worker_thread_lock((PHandleHeader)Handle);

	*Cmd=CMD_FREE_DATA;
	//change by zxz for x20
	if(strncmp(h->cName(h), "USBSTD", 6) == 0)
		send_command_full((PHandleHeader)Handle, Cmd, Buffer, 1024, 0, TimeOutMS + 100000,  NULL);
	else
		send_command_full((PHandleHeader)Handle, Cmd, Buffer, 1024, 0, TimeOutMS, NULL);

	if(ret>0 && compressed)
	{
		if(LZO_E_OK==lzo1x_decompress((const unsigned char*)dataBuffer, dataLen, (unsigned char*)Buf, (unsigned int*)&BufLen, NULL))
		{
			if(BufLen!=oriLen) ret=0;
		}
		else
			ret=0;
	}
	if(compressed) udk_free(dataBuffer);
	if(ret>0)
		return oriLen;
	
	//worker_thread_unlock((PHandleHeader)Handle);
	return ret;
}


 int ZEMBPRO_READDB(UDK_HANDLE Handle, UDK_U8 DBFlag, int BlockSize, char *Buffer, int *BufferSize)
{
	int ret;
	UDK_U16 cmd=CMD_DB_RRQ;
	Buffer[0]=DBFlag;
	ret=ZEMBPRO_READDATA2(Handle, &cmd, Buffer, *BufferSize, 1, BlockSize, 1000);
	return ret;
}

 int ZEMBPRO_READDATA(UDK_HANDLE Handle, unsigned short *Cmd, char *Buf, int BufLen, int SendLen, int BlockSize, int TimeOutMS)
{
	UDK_U16 cmd=*Cmd;
	if(cmd==CMD_DB_RRQ || cmd==CMD_QUERY_FIRMWARE || cmd==CMD_ATTLOG_RRQ || cmd==CMD_OPLOG_RRQ || cmd==CMD_USERTEMP_RRQ || 
		cmd==CMD_READ_NEW || cmd==CMD_OPTIONS_RRQ || cmd==CMD_READTMP || 
		cmd == CMD_APP_PULL_COMPANY_REPORTS || cmd == CMD_APP_PULL_PERSONAL_REPORTS ||
		cmd == CMD_APP_PULL_ATT_RECOREDS)
	{
		int ret=ZEMBPRO_READDATA2(Handle, Cmd, Buf, BufLen, SendLen, BlockSize<512-16?8*1024:BlockSize, TimeOutMS);
		if(*Cmd==CMD_ACK_UNKNOWN) //not supported new command
		{
			*Cmd=cmd;
			ret=ZEMBPRO_READDATA1(Handle, Cmd, Buf, BufLen, SendLen, UDK_TRUE, TimeOutMS);
		}
		else if(*Cmd==CMD_ACK_ERROR)
			return 0;
		return ret;
	}
	else	//ֱ��ȡ���
		return ZEMBPRO_READDATA1(Handle, Cmd, Buf, BufLen, SendLen, UDK_TRUE, TimeOutMS);
}

UDK_HANDLE ZEMBPRO_INITEX(const char *CommLib,const char *Address, unsigned short Port, int Speed, ReadCallBack ReadFun, void* Param, int CommKey, char *Telephone)
{
	PHandleHeader h;
	int i = 0;
	unsigned short cmd;
	UDK_U32 tid;
	UDK_U32 interTime = 0;

	int TmpCommKey = 0;//zhc add TmpCommKey 2010-06-01, �ı�ȡ�β�CommKey�ĵ�ַ��Ϊ����ݣ�������߳�ʹ��ʱ�������ڴ���ʳ��?
	h=(PHandleHeader)udk_malloc(sizeof(THandleHeader));if(!h) return NULL;
	init_commpro_object(h);
	
	int ret = comm_module_interface_init(h,CommLib);
	if(ret){
		LastError = ret;
		udk_free(h); 
		return 0;
	}
	//dsl 2007.7.5
	if (Telephone)
	{
		if (strlen(Telephone) > 0)
		{
			strcpy(h->Telephone, Telephone);
			h->Dialup = 1;
		}
	}
	h->slOptionCache = slCreate("=");
	h->ReadCallBackFunc=ReadFun;
	LastError=-1;
	h->RecvBuffer = (char*)udk_malloc(MAX_BUFFER_LEN_TCP);
	h->RecvBufferLength = MAX_BUFFER_LEN_TCP;
	h->SendBuffer = (char*)udk_malloc(MAX_BUFFER_LEN_TCP);
	h->SendBufferLength = MAX_BUFFER_LEN_TCP;
	h->commparams.PackageSize = 5*1024;
	h->commparams.Quirks|=COMM_PARAM_QUIRK_DEBUG;
	h->commparams.StartTag = PROTO_START_TAG;
	h->commparams.EndTag = PROTO_END_TAG;
	h->commparams.DestAddr = Port;
	h->commparams.TimeOutMS = 1000;
	h->cHandle=h->cInit(&h->commparams,Address, Port, Speed);
	if(!h->cHandle)
	{
		goto initex_failed;
	}
	LastError=-3;
	h->Param=Param;
	h->SessionID=0;
	h->ReplyID=0;
	h->DataDelay=0;
	h->BlockSize=0;

	h->ReadThread=new CCommproThread(h);
	cmd=CMD_CONNECT;
	if(h->ReadThread)
	{
		LastError=-4;
		//dsl
		if (h->Dialup)
		{
			if (ModemDialup(h))
			{
				//MODEM��ͨ��,��绰Ϊ��.
				memset(h->Telephone, 0, sizeof(h->Telephone));
				i=ZEMBPRO_SENDCMD(h, &cmd, NULL, 0, 0, 9000);
				//i=ZEMBPRO_SENDCMD(h, &cmd, NULL, 0, 0, 2000);
			}
		}
		else
		{				
			i=ZEMBPRO_SENDCMD(h, &cmd, NULL, 0, 0, 5000);
		}
		
		if(cmd==CMD_ACK_OK)
		{
			h->SessionID=i;
			h->ReplyID=1;
		}
		else if(cmd==CMD_ACK_UNAUTH)
		{
			LastError=-6;
			h->SessionID=i;
			h->ReplyID=1;
			cmd=CMD_AUTH;
//			CommKey=789456; //Test Only

			CommKey=MakeKey(CommKey, h->SessionID);

			TmpCommKey = CommKey; //add by zhc 2010-06-01
			i=ZEMBPRO_SENDCMD(h, &cmd, (char*)&TmpCommKey, 4, 4, 5000);
		}		
		else
		{
			if (h->Dialup)
			{
				LastError = ATCMD_ERR;
			}
			else
			{
				LastError=-7;
			}
		}
	}
	else
		LastError=-5;

	if(cmd!=CMD_ACK_OK)
	{
		LastError = ZKEMSDK_ERR_READ_MODE;
		goto initex_failed;
	}
	LastError=0;
	return h;
initex_failed:
	if(h->ReadThread) {delete h->ReadThread;h->ReadThread=NULL;}
	if(h->cHandle){h->cClose(h->cHandle);h->cHandle=NULL;}
	if(h->cLibHandle) {udk_dlclose(h->cLibHandle);h->cLibHandle=NULL;}
	if(h->RecvBuffer) {udk_free(h->RecvBuffer);h->RecvBuffer = NULL;}
	if(h->SendBuffer) {udk_free(h->SendBuffer);h->SendBuffer = NULL;}
	if(h->slOptionCache){slFree(h->slOptionCache);h->slOptionCache=NULL;}	
	if(h->requestmutex) {delete h->requestmutex;h->requestmutex = NULL;}
	if(h->requestcond) {delete h->requestcond;h->requestcond = NULL;}
	udk_free(h);
	return NULL;
}


int READDATA_PICE_BY_PICE(UDK_HANDLE Handle, unsigned short *Cmd, char *SendBuf, int SendLen, char *Buf,  int BufLen, UDK_BOOL CheckData, int TimeOutMS, int *RecLen)
{
	int errorflag = 0;
	int tmpcount = 0;
	int curcount = 0;
	int readcount = 0;
	int readlen = 0;
	int trycnt;
	int curlen = 0;
	int res = 0;
	PHandleHeader h=(PHandleHeader)Handle;
	char *buffer = (char*)udk_malloc(400*1024);
	PCmdHeader pchd=(PCmdHeader)buffer;
	if(!buffer) {
		UDK_LOGW("out of mem\n");
		return 0;
	}
	worker_thread_lock((PHandleHeader)Handle);
	int ret=send_command_full_async((PHandleHeader)Handle, Cmd, SendBuf, MAX_BUFFER_LEN, 0, TimeOutMS, NULL);

	if(*Cmd == CMD_ACK_OK)
	{
		memcpy(&tmpcount, SendBuf, 4);
		while(curcount < tmpcount) {
			if(tmpcount - curcount > 200) {
				readcount = 200;
			} else {
				readcount = tmpcount - curcount;
			}
			for(trycnt = 0; trycnt < 3; trycnt++) {
				readlen = ReadBlock_LargeTmp(Handle, buffer, curcount, readcount, 1000*100);
				if(readlen > 0) {
					memcpy(SendBuf + curlen, buffer, readlen);
					curlen += readlen;
					errorflag = 0;
					curcount += readcount;
					break;
				}
				errorflag = 1;
			}
			if(errorflag) {
				break;
			}
		}
		*Cmd = CMD_FREE_LARGE_TMP;
		send_command_full_async((PHandleHeader)Handle, Cmd, SendBuf, MAX_BUFFER_LEN, 0, TimeOutMS,  NULL);//�ͷ���Դ
	}//end if(*Cmd==CMD_PREPARE_DATA)
	
	if(errorflag)//�������ʱ,����ʣ�����
	{
		while(h->cRead(h->cHandle, buffer, MAX_BUFFER_LEN, 500));
		res = -1;
		*Cmd = CMD_ACK_ERROR_DATA;
	}
	else
	{
		*Cmd = CMD_ACK_OK;
		res = curlen;
	}

	udk_free(buffer);
	worker_thread_lock((PHandleHeader)Handle);
	return res;
}

int ReadBlock_LargeTmp(UDK_HANDLE Handle, char* buf, int currentcnt, int readcnt, int TimeOutMS)
{
	int res = 0;
	int cmd = 0;
	int datalength = 0;
	int rlen = 0;
	int curlen = 0;

	unsigned short checksum = 0;
	unsigned short cs;
	int errorflag = 0;
	char* buffer = (char*)udk_malloc(64*1024);;
	PCmdHeader pchd=(PCmdHeader)buffer;
	PHandleHeader h=(PHandleHeader)Handle;
	
	memcpy(buf, &currentcnt, 4);
	memcpy(buf+4, &readcnt, 4);
	cmd = CMD_DATA_LARGE_TMP;
	
	worker_thread_lock((PHandleHeader)Handle);
	res = send_command_full_async((PHandleHeader)Handle, (unsigned short*)&cmd, buf, MAX_BUFFER_LEN, 8, TimeOutMS,  NULL);
	if(cmd == CMD_PREPARE_DATA) {
		memcpy(&datalength, buf, 4);
		memcpy(&checksum, buf+4, 2);
		while(1) {
			rlen=h->cRead(h->cHandle, buffer, sizeof(buffer), TimeOutMS);
			if(rlen <= 0) {
				udk_msleep(10);
				continue;
			}

			if(pchd->Command == CMD_DATA) {
				cs=pchd->CheckSum;
				pchd->CheckSum=0;
				if(cs != in_chksum(buffer, rlen))
				{
					errorflag = 1;
					break;
				}
				memcpy(buf + curlen, buffer+sizeof(TCmdHeader), rlen - sizeof(TCmdHeader));
				curlen += (rlen - sizeof(TCmdHeader));
				continue;
			}
			/*finish*/
			if(pchd->Command == CMD_ACK_OK) {
				break;
			}
			errorflag = 1;
			break;
		}
	}
	if(checksum == in_chksum(buf, curlen)) {
		res = curlen;
	} else {
		errorflag = 1;
	}

	if(errorflag || datalength != curlen)//�������ʱ,����ʣ�����
	{
		while(h->cRead(h->cHandle, buffer, MAX_BUFFER_LEN, 500));
		res = -1;
	} 
	udk_free(buffer);
	
	worker_thread_unlock((PHandleHeader)Handle);
	return res;
}

//even this api is ugly,just leave it now
void SetBuffLen(UDK_HANDLE Handle, int bufferlen)
{
	PHandleHeader h = (PHandleHeader)Handle;
	h->RecvBufferLength = bufferlen;
}

static int SENDBROADCAST(UDK_HANDLE Handle,char *SendBuf,int SendBufLen,char* RecvBuf,int *RecvBufLen, int TimeOutMS, int Wait, int *noRep)
{
	int res=0, retsend;
	PHandleHeader h=(PHandleHeader)Handle;
	int buflen=0, rlen, rl, count=0;
	const char rn[3]={'\r', '\n','\0'};
	char rowDatas[1024] = {0}, rowBuf[1024] = {0};
	int RecvBufSize = *RecvBufLen;
	UDK_DWORD msStartTime =0,msCurrentTime = 0;

	*RecvBufLen=0;
	retsend = h->cSend(h->cHandle,SendBuf, SendBufLen);
	if(!retsend) res = ERR_SEND_FAILED;

	msStartTime = udk_get_ticks();
	while(1)
	{	
		msCurrentTime = udk_get_ticks();
		if((msCurrentTime-msStartTime)>TimeOutMS)
		{
			break;
		}

		if(msCurrentTime <msStartTime)
		{
			UDK_LOGE("GetTickCount error\n");
			break;
		}
		udk_msleep(30);
		memset(rowDatas, 0x00, 1024);
		buflen = 1024;
		rlen = h->cRead(h->cHandle, rowDatas, buflen, 1000);
		//filter only std broadcast
		if(rlen >10&&rowDatas[0]=='X'){		
			char *pos = strstr(rowBuf, rowDatas);
			if (!pos){
				rl=sprintf(rowBuf, "%s%s", rowDatas, rn);					
				if(*RecvBufLen<=RecvBufSize){
					memcpy(RecvBuf+(*RecvBufLen), rowBuf, rl);
					*RecvBufLen+=rl;
					count++;
				}else{
					UDK_LOGW("Buffer too small *RecvBufLen=%d RecvBufSize=%d\n",*RecvBufLen,RecvBufSize);
				}
				UDK_LOGD("count=  %d\n", count);
			}
		}
	}
	
	return count;
}
int 
	ZEMBPRO_BROADCAST(char *CommLib, const char *Address, char *SendBuf,int SendBufLen,char* RecvBuf, int *RecvBuflen, int TimeOutMS)
{
	PHandleHeader h=(PHandleHeader)udk_malloc(sizeof(THandleHeader));
	int ret;
	int i = 0;
	if(!h) return 0;
	init_commpro_object(h);
	ret = comm_module_interface_init(h,CommLib);
	if(ret){
	  UDK_LOGE("Can't find %s comm library\n",CommLib);
	  udk_free(h); 
	  return 0;
	}
	
	h->commparams.PackageSize = 64*1024;
	h->commparams.Quirks|=COMM_PARAM_QUIRK_DEBUG;
	h->commparams.StartTag = PROTO_START_TAG;
	h->commparams.EndTag = PROTO_END_TAG;
	h->commparams.DestAddr = BROADCAST_PORT;
	h->commparams.TimeOutMS = 1000;	
	//comment out following flag if you don't wanna dump more data
	h->commparams.Quirks|=COMM_PARAM_QUIRK_DEBUG;
	
	h->cHandle=h->cInit(&h->commparams,Address, BROADCAST_PORT, 0);
	if(!h->cHandle)
	{
		UDK_LOGE("%s init failed\n",CommLib);
		goto broadcast_init_failed;
	}
	ret = SENDBROADCAST(h, SendBuf, SendBufLen,RecvBuf,RecvBuflen,TimeOutMS, 0, NULL);
	
broadcast_init_failed:
	if(h->cHandle){h->cClose(h->cHandle);h->cHandle=NULL;}
	if(h->cLibHandle) {udk_dlclose(h->cLibHandle);h->cLibHandle=NULL;}	
	if(h->requestmutex) {delete h->requestmutex;h->requestmutex = NULL;}
	if(h->requestcond) {delete h->requestcond;h->requestcond = NULL;}
	udk_free(h);
	return ret;
}

UDK_DLLIMPORT void ZEMBPRO_SetDownloadProcessCallBack(UDK_HANDLE Handle, void *pCallBackFun)
{
	PHandleHeader h=(PHandleHeader)Handle;
	if(NULL == h)
	{
		return;
	}
	h->pCBDownloadProcess = (PCBUDKDownloadProcess)pCallBackFun;
}

